package com.mall.seckill.service.impl;

import com.mall.seckill.model.SeckillGoods;
import com.mall.seckill.mapper.SeckillGoodsMapper;
import com.mall.seckill.model.SeckillOrder;
import com.mall.seckill.service.ISeckillGoodsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.mall.seckill.service.ISeckillOrderService;
import org.apache.commons.lang3.ObjectUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 秒杀商品表 服务实现类
 * </p>
 *
 * @author Darren
 * @since 2022-09-28
 */
@Service
public class SeckillGoodsServiceImpl extends ServiceImpl<SeckillGoodsMapper, SeckillGoods> implements ISeckillGoodsService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ISeckillOrderService seckillOrderService;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 库存不足
     */
    public static final int STORE_NOT_FULL = 0;
    /**
     * 库存足够,下单成功
     */
    public static final int STORE_ORDER_SUCCESS = 1;

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean addSeckillGoods(SeckillGoods seckillgoods) {
        return super.save(seckillgoods);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean deleteById(String id) {
        return super.removeById(id);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean updateSeckillGoods(SeckillGoods seckillgoods) {
        return super.updateById(seckillgoods);
    }

    @Override
    public SeckillGoods getById(String id) {
        return super.getById(id);
    }

    @Override
    public List<SeckillGoods> selectList(SeckillGoods seckillgoods) {
        return super.list(getQueryWrapper(seckillgoods));
    }

    @Override
    public Page<SeckillGoods> selectPage(Page<SeckillGoods> page, SeckillGoods seckillgoods) {
        return super.page(page, getQueryWrapper(seckillgoods));
    }

    @Override
    public void isolation(String uri) {
        // 锁定商品
        SeckillGoods seckillGoods = new SeckillGoods();
        seckillGoods.setIsLock(1);
        int update = this.baseMapper.update(seckillGoods, new LambdaQueryWrapper<SeckillGoods>()
                .eq(SeckillGoods::getIsLock, 0)
                .eq(SeckillGoods::getId, uri)
                .gt(SeckillGoods::getNum, 0)
                .gt(SeckillGoods::getStoreCount, 0));
        if (update > 0) {
            // 数据存入缓存隔离(需要控制集群环境问题，所以定时任务分片只设置成1个分片)
            seckillGoods = this.getById(uri);
            redisTemplate.boundHashOps("HotSeckillGoods").increment(uri, seckillGoods.getNum());
        }
    }

    @Override
    public int add(Map<String, Object> dataMap) {
        String username = dataMap.get("username").toString();
        String id = dataMap.get("id").toString();
        Integer num = Integer.valueOf(dataMap.get("num").toString());

        // 使用redisson分布式锁
        RLock lock = redissonClient.getLock("SeckillLock");
        lock.lock();

        try {
            Object count = redisTemplate.boundHashOps("HotSeckillGoods").get(id);
            // 库存不足
            if (count == null || Integer.parseInt(count.toString()) < num) {
                // 排除排队标志
                redisTemplate.delete("OrderQueue" + username);
                return STORE_NOT_FULL;
            }

            // 库存足够,查询商品信息
            SeckillGoods seckillGoods = this.getById(id);
            // 添加订单
            Date date = new Date();
            SeckillOrder seckillOrder = new SeckillOrder();
            seckillOrder.setId(UUID.randomUUID().toString().replace("-", ""));
            seckillOrder.setSeckillGoodsId(id);
            seckillOrder.setCreateTime(date);
            seckillOrder.setStatus(0);
            seckillOrder.setMoney(seckillGoods.getPrice() * num);
            seckillOrder.setNum(num);
            seckillOrder.setPayTime(date);
            seckillOrder.setUsername(username);
            seckillOrderService.save(seckillOrder);

            // 同步商品数据
            SeckillGoods updateSeckillGoods = new SeckillGoods();
            updateSeckillGoods.setId(id);
            updateSeckillGoods.setNum(seckillGoods.getNum() - num);
            updateSeckillGoods.setStoreCount(seckillGoods.getStoreCount() - num);
            this.updateById(updateSeckillGoods);

            // 库存递减
            Long lastCount = redisTemplate.boundHashOps("HotSeckillGoods").increment(id, -num);
            if (lastCount == null || lastCount == 0) {
                // TODO 将当前商品添加到Redis布隆过滤器,用户下次抢购该商品,去布隆过滤器中判断,该商品是否在布隆过滤器中,如果在,则表明售罄

                // 删除redis缓存
                redisTemplate.boundHashOps("HotSeckillGoods").delete(id);
            }

            // 移除排队标识
            redisTemplate.delete("OrderQueue" + username);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

        return STORE_ORDER_SUCCESS;
    }

    public LambdaQueryWrapper<SeckillGoods> getQueryWrapper(SeckillGoods seckillgoods) {
        return Wrappers.<SeckillGoods>lambdaQuery()
                .eq(ObjectUtils.isNotEmpty(seckillgoods.getId()), SeckillGoods::getId, seckillgoods.getId())
                .eq(ObjectUtils.isNotEmpty(seckillgoods.getSupId()), SeckillGoods::getSupId, seckillgoods.getSupId())
                .eq(ObjectUtils.isNotEmpty(seckillgoods.getSkuId()), SeckillGoods::getSkuId, seckillgoods.getSkuId())
                .eq(ObjectUtils.isNotEmpty(seckillgoods.getName()), SeckillGoods::getName, seckillgoods.getName())
                .eq(ObjectUtils.isNotEmpty(seckillgoods.getImages()), SeckillGoods::getImages, seckillgoods.getImages())
                .eq(ObjectUtils.isNotEmpty(seckillgoods.getPrice()), SeckillGoods::getPrice, seckillgoods.getPrice())
                .eq(ObjectUtils.isNotEmpty(seckillgoods.getSeckillPrice()), SeckillGoods::getSeckillPrice, seckillgoods.getSeckillPrice())
                .eq(ObjectUtils.isNotEmpty(seckillgoods.getNum()), SeckillGoods::getNum, seckillgoods.getNum())
                .eq(ObjectUtils.isNotEmpty(seckillgoods.getStoreCount()), SeckillGoods::getStoreCount, seckillgoods.getStoreCount())
                .eq(ObjectUtils.isNotEmpty(seckillgoods.getContent()), SeckillGoods::getContent, seckillgoods.getContent())
                .eq(ObjectUtils.isNotEmpty(seckillgoods.getActivityId()), SeckillGoods::getActivityId, seckillgoods.getActivityId())
                ;
    }
}
